package com.sid

import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.WritableNativeMap
import com.sid.SIDUtils.INVALID_PARAMS
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import sberid.sdk.auth.model.StandName
import sberid.sdk.global.models.CustomFonts
import sberid.sdk.global.models.LinkInformation
import sberid.sdk.global.models.OIDCParams
import sberid.sdk.init.SID
import sberid.sdk.ui.models.root.UIColorMode

class SidModule(reactContext: ReactApplicationContext) :
  ReactContextBaseJavaModule(reactContext) {

  companion object {

    private const val NAME = "Sid"
    private const val STATE = "state"
    private const val NONCE = "nonce"
    private const val SCOPE = "scope"
    private const val CLIENT_ID = "client_id"
    private const val CODE = "code"
    private const val IS_SUCCESS = "is_success"
    private const val ERROR_DESCRIPTION = "error_description"
    private const val ERROR_CODE = "error_code"
    private const val PARTNER_NAME = "partner_name"
    private const val THEME_COLOR_LIGHT = "theme_color_light"
    private const val THEME_COLOR_DARK = "theme_color_dark"
    private const val PARTNER_PROFILE_URL = "partner_profile_url"
    private const val IS_SHOW_ERROR_ON_MAIN = "is_show_error_on_main"
    private const val REDIRECT_URI = "redirect_uri"
    private const val CODE_CHALLENGE = "code_challenge"
    private const val CODE_CHALLENGE_METHOD = "code_challenge_method"
    private const val CUSTOM_TAB_REDIRECT_URI = "custom_tab_redirect_uri"
    private const val URI_SCHEME = "uri_scheme"
    private const val LOGIN_HINT = "login_hint"
    private const val PARTNER_COLOR_INT = "partner_color_int"
    private const val OPEN_IN = "open_in"
    private const val PARTNER_LINK = "partner_link"
    private const val OIDC_URL = "oidc_url"
    private const val TYPE_AUTH = "type_auth"
    private const val CREATE_TIME_APP_TOKEN = "create_time_app_token"
    private const val UI_COLOR_MODE = "ui_color_mode"
    private const val MEDIUM_RES_ID = "medium_res_id"
    private const val SEMI_BOLD_RES_ID = "semi_bold_res_id"

    private const val PSI_CLOUD_STAND = "psi_cloud"
    private const val PROM_STAND = "prom"
    private const val PSI_STAND = "psi"
    private const val IFT_STAND = "ift"
    private const val IFT_CLOUD_STAND = "ift_cloud"

    private const val LIGHT = "light"
    private const val DARK = "dark"
    private const val AUTO = "auto"

    private const val EMPTY_ERROR = "IS EMPTY"
    private const val CREATE_LOGIN_MANAGER_ERROR =
      "You can use there stands - $PROM_STAND, $PSI_STAND, $PSI_CLOUD_STAND, $IFT_STAND, $IFT_CLOUD_STAND"
    private const val CONTEXT_ERROR = "There isn't any context"
    private const val ACTIVITY_ERROR = "There isn't any activity"
    private const val APP_TOKEN_ERROR = "There isn't any token"
    private const val OPTION_ERROR = "There isn't correct option"
  }

  override fun getName(): String = NAME
  private var clientID = ""
  private var partnerName: String? = null
  private var themeColorLight: Int? = null
  private var themeColorDark: Int? = null
  private var partnerProfileUrl: String? = null
  private var isShowErrorOnMain: Boolean = false
  private var standType: String = PROM_STAND

  //Initializer


  @ReactMethod
  fun initialize(
    clientId: String,
    partnerName: String?,
    themeColorLight: String?,
    themeColorDark: String?,
    partnerProfileUrl: String?,
    isShowErrorOnMain: Boolean,
    standType: String
  ) {
    this.clientID = clientId
    this.partnerName= partnerName
    this.partnerProfileUrl = partnerProfileUrl
    this.themeColorLight = kotlin.runCatching { Color.parseColor(themeColorLight) }.getOrNull()
    this.themeColorDark = kotlin.runCatching { Color.parseColor(themeColorDark) }.getOrNull()
    this.isShowErrorOnMain = isShowErrorOnMain
    this.standType = PROM_STAND
    val applicationContext = currentActivity?.application ?: return

    SID.Initializer.initialize(
      application = applicationContext,
      clientId = clientId,
      partnerName = partnerName,
      partnerProfileUrl = partnerProfileUrl,
      themeColorLight = this.themeColorLight,
      themeColorDark = this.themeColorDark,
      isShowErrorOnMain = isShowErrorOnMain
    )

    createLoginManager(nameOfStand = standType)
  }

  //Login

  @ReactMethod
  private fun createLoginManager(nameOfStand: String) {
    val stand = when (nameOfStand) {
      PROM_STAND -> StandName.PROM
      PSI_STAND -> StandName.ESA_PSI
      PSI_CLOUD_STAND -> StandName.CLOUD_PSI
      IFT_STAND -> StandName.ESA_IFT
      IFT_CLOUD_STAND -> StandName.CLOUD_IFT
      else -> StandName.PROM
    } ?: return

    SID.Login.createLoginManager(
      stand
    )
  }

  @ReactMethod
  private fun createUriForLogin(
    clientId: String,
    scope: String,
    state: String,
    nonce: String,
    redirectUri: String,
    customTabRedirectUri: String?,
    codeChallenge: String?,
    codeChallengeMethod: String?,
    uriScheme: String?,
    loginHint: String?,
  ): String {
    return SID.Login.createUriForLogin(
      clientId = clientId,
      scope = scope,
      state = state,
      nonce = nonce,
      redirectUri = redirectUri,
      customTabRedirectUri = customTabRedirectUri,
      codeChallenge = codeChallenge,
      codeChallengeMethod = codeChallengeMethod,
      uriScheme = null,
      loginHint = loginHint
    ).toString()
  }

  @ReactMethod
  private fun loginWithID(uri: Uri) {
    val activity = currentActivity as? FragmentActivity ?: run {
      loginWithIDContext(uri, currentActivity?.applicationContext)
      return
    }

    SID.Login.loginWithID(
      activity,
      uri
    )
  }

  @ReactMethod
  fun auth(
    scope: String,
    state: String,
    nonce: String,
    redirectUri: String,
    ssoBaseUrl: String?, //хз что за параметр
    codeChallenge: String?,
    codeChallengeMethod: String?,
    loginHint: String?
  ){
    val url = SID.Login.createUriForLogin(
      clientId = clientID,
      scope = scope,
      state = state,
      nonce = nonce,
      redirectUri = redirectUri,
      uriScheme = ssoBaseUrl, //Не знаю куда тут это поставить расхождение на платформах
      codeChallenge = codeChallenge,
      codeChallengeMethod = codeChallengeMethod,
      loginHint = loginHint
    )
    loginWithID(url)
  }

  private fun loginWithIDContext(uri: Uri, applicationContext: Context?) {
    val context = applicationContext ?: return

    SID.Login.loginWithID(
      context,
      uri
    )
  }

  @ReactMethod
  fun loginWithIDWeb(
    scope: String,
    state: String,
    nonce: String,
    redirectUri: String,
    svcRedirectUrlString: String,
    ssoBaseUrl: String? = null,
    codeChallenge: String? = null,
    codeChallengeMethod: String? = null,
    loginHint: String? = null,
    callback: Callback
  ) {
    val context = currentActivity?.application ?: run {
      callback(false)
      return
    }

    val url = SID.Login.createUriForLogin(
      clientId = clientID,
      scope = scope,
      state = state,
      nonce = nonce,
      redirectUri = redirectUri,
      customTabRedirectUri = svcRedirectUrlString,
      codeChallenge = codeChallenge,
      codeChallengeMethod = codeChallengeMethod,
      loginHint = loginHint
    )

    SID.Login.loginWithIDToCustomTab(
      context,
      url
    )
  }

  @ReactMethod
  fun logout() {
    SID.Login.logout()
  }

  @ReactMethod
  fun autoAuth(
    scope: String,
    state: String,
    nonce: String,
    redirectUri: String,
    ssoBaseUrl: String? = null,
    codeChallenge: String? = null,
    codeChallengeMethod: String? = null,
    loginHint: String? = null,
    callback: Callback
  ) {
    val context = currentActivity?.application ?: run {
      callback(false)
      return
    }

    val url = SID.Login.createUriForLogin(
      clientId = clientID,
      scope = scope,
      state = state,
      nonce = nonce,
      redirectUri = redirectUri,
      customTabRedirectUri = ssoBaseUrl,
      codeChallenge = codeChallenge,
      codeChallengeMethod = codeChallengeMethod,
      loginHint = loginHint
    )

    /*		val authResult = SID.Login.autoAuth(
          context,
          url
        )

        callback(authResult)*/
  }

  @ReactMethod
  fun getIDAuthResult(
    deepLink: String,
    completion: Callback
  ) {
    val newIntent = Intent().apply {
      data = Uri.parse(deepLink)
    }
    val result = SID.Login.getIDAuthResult(newIntent)

    val resultMap = WritableNativeMap().apply {
      putString(CODE, result.authCode)
      putString(STATE, result.state)
      putString(NONCE, result.nonce)
      putBoolean(IS_SUCCESS, result.isSuccess ?: false)
      putString(ERROR_DESCRIPTION, result.errorDescription)
      putString(ERROR_CODE, result.errorCode)
    }
    completion(resultMap)
  }

  @ReactMethod
  fun sendOtherAuth(nameAuthService: String, isSuccess: Boolean = true, isUserNew: Boolean? = null) {
    SID.Login.sendOtherAuth(nameAuthService, isSuccess, isUserNew)
  }

  @ReactMethod
  fun getSeamlessUriScheme(uri: String): String? {
    return SID.Login.getSeamlessUriScheme(uri = uri.toUri())
  }

  @ReactMethod
  fun setUserID(userID: String) {
    SID.Login.setUserID(userID = userID)
  }

  @ReactMethod
  fun setClientID(clientId: String) {
    val applicationContext = currentActivity?.application ?: return
    SID.Initializer.initialize(
      application = applicationContext,
      clientId = clientId,
      partnerName = partnerName,
      partnerProfileUrl = partnerProfileUrl,
      themeColorLight = themeColorLight,
      themeColorDark = themeColorDark,
      isShowErrorOnMain = isShowErrorOnMain
    )
  }


  //AppToken
  @ReactMethod
  fun updateAppToken(
    scope: String,
    clientId: String,
    state: String,
    nonce: String,
    redirectUri: String,
    codeChallenge: String?,
    codeChallengeMethod: String?,
    completion: Callback
  ) {
    val params = OIDCParams(
      scope = scope,
      clientId = clientId,
      state = state,
      nonce = nonce,
      redirectUri = redirectUri,
      codeChallenge = codeChallenge,
      codeChallengeMethod = codeChallengeMethod
    )
    SID.AppToken.updateAppToken(params)
  }

  @ReactMethod
  fun checkAppTokenForValid(completion: Callback) {
    SID.AppToken.checkAppTokenForValid()
  }

  @ReactMethod
  fun subscribeToAppTokenEvents(callback: Callback) {
    val activity = currentActivity as? AppCompatActivity ?: run {
      callback(INVALID_PARAMS + ACTIVITY_ERROR)
      return
    }
    SID.AppToken.subscribeToEvents(activity) { event ->
      callback(event.toString())
    }
  }

  @ReactMethod
  fun goToPartnerSSO(
    partnerWebLink: String,
    partnerClientId: String?,
    partnerColor: String?,
    openIn: String?,
    completion: Callback
  ) {
    val activity = currentActivity as? FragmentActivity ?: run {
      return
    }
    val linkInformation = LinkInformation(
      webLink = partnerWebLink,
      clientId = partnerClientId,
      partnerColorInt = partnerColor?.toIntOrNull(),
      openIn = openIn
    )
    SID.AppToken.goToPartnerSSO(activity, linkInformation)
  }

  @ReactMethod
  fun getApp2SdkToken(
    clientId: String,
    partnerLink: String,
    callback: Callback
  ) {
    CoroutineScope(Dispatchers.IO).launch {
      val result = SID.AppToken.getApp2SdkToken(clientId = clientId, partnerLink = partnerLink) ?: run {
        callback(EMPTY_ERROR + APP_TOKEN_ERROR)
        return@launch
      }
      val resultMap = WritableNativeMap().apply {
        putString(OIDC_URL, result.oidcUrl)
        putString(TYPE_AUTH, result.typeAuth.toString())
        putDouble(CREATE_TIME_APP_TOKEN, result.createTimeAppToken.toDouble())
      }
      callback(resultMap)
    }
  }

  //ELK
  @ReactMethod
  fun subscribeToAnalytic(callback: Callback) {
    val activity = currentActivity as? AppCompatActivity ?: run {
      callback(INVALID_PARAMS + ACTIVITY_ERROR)
      return
    }
    SID.ELK.subscribeToAnalytic(activity) { event ->
      callback(event)
    }
  }

  @ReactMethod
  fun setUIMode(
    uiModeString: String
  ) {
    val uiMode = when (uiModeString) {
      LIGHT -> UIColorMode.LIGHT
      DARK -> UIColorMode.DARK
      AUTO -> UIColorMode.AUTO
      else -> return
    }

    SID.ELK.setUIMode(uiMode)
  }

  @ReactMethod
  fun setCustomFonts(
    mediumResID: Int?,
    semiBoldResID: Int?,
    callback: Callback
  ) {
    val customFonts = CustomFonts(
      semiBoldResID = semiBoldResID,
      mediumResID = mediumResID
    )
    SID.ELK.setCustomFonts(customFonts)
  }

}
